
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABORQUAT_H__
#define __LABORQUAT_H__



#include <math.h>
#include "../LaborCoreDef.h"
#include "../Math/LaborMathDecl.h"
#include "LaborPoint3.h"
NAMESPACE_LABOR3D_BEGIN
class CLPlane;
class CLMatrix3x3;
class CLMatrix4x4;
class  LABORCORE_API CLQuat
{
public:
	inline CLQuat (
		float fW = 1.0f,
		float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f)
	{
		w = fW;
		x = fX;
		y = fY;
		z = fZ;
	}
	inline CLQuat (const CLQuat& rkQ)
	{
		w = rkQ.w;
		x = rkQ.x;
		y = rkQ.y;
		z = rkQ.z;
	}
	/// Construct a CLQuat from a rotation matrix
	inline CLQuat(const CLMatrix3x3& rot)
	{
		fromRotationMatrix(rot);
	}
	/// Construct a CLQuat from an angle/axis
	inline CLQuat( float& rfAngle, const CLPoint3& rkAxis )
	{
		fromAngleAxis(rfAngle, rkAxis);
	}
	/// Construct a CLQuat from 3 orthonormal local axes
	inline CLQuat(const CLPoint3& xAxis, const CLPoint3& yAxis, const CLPoint3& zAxis)
	{
		fromAxes(xAxis, yAxis, zAxis);
	}
	/// Construct a CLQuat from 3 orthonormal local axes
	inline CLQuat(CLPoint3* akAxis)
	{
		fromAxes(akAxis);
	}

	inline CLQuat& rotationYawPitchRoll(float yaw, float pitch, float roll)
	{
		const float angX(pitch * 0.5f), angY(yaw * 0.5f), angZ(roll * 0.5f);
		float sx, sy, sz;
		float cx, cy, cz;
		sx = sinf(angX); cx = cosf(angX);
		sy = sinf(angY); cy = cosf(angY);
		sz = sinf(angZ); cz = cosf(angZ);

		x = sx * cy * cz + cx * sy * sz;
		y = cx * sy * cz - sx * cy * sz;
		z = cx * cy * sz - sx * sy * cz;
		w = sx * sy * sz + cx * cy * cz;

		return *this;
	}

	void fromRotationMatrix (const CLMatrix3x3& kRot);
	void toRotationMatrix (CLMatrix3x3& kRot) const;
	void fromAngleAxis (const float& rfAngle, const CLPoint3& rkAxis);
	void toAngleAxis (float& rfAngle, CLPoint3& rkAxis) const;
	void fromAxes (const CLPoint3* akAxis);
	void fromAxes (const CLPoint3& xAxis, const CLPoint3& yAxis, const CLPoint3& zAxis);
	void toAxes (CLPoint3* akAxis) const;
	void toAxes (CLPoint3& xAxis, CLPoint3& yAxis, CLPoint3& zAxis) const;
	/// Get the local x-axis
	CLPoint3 xAxis(void);
	/// Get the local y-axis
	CLPoint3 yAxis(void);
	/// Get the local z-axis
	CLPoint3 zAxis(void);

	inline CLQuat& operator= (const CLQuat& rkQ)
	{
		w = rkQ.w;
		x = rkQ.x;
		y = rkQ.y;
		z = rkQ.z;
		return *this;
	}
	CLQuat operator+ (const CLQuat& rkQ) const;
	CLQuat operator- (const CLQuat& rkQ) const;
	CLQuat operator* (const CLQuat& rkQ) const;
	CLQuat operator* (float fScalar) const;
	friend CLQuat operator* (float fScalar,
		const CLQuat& rkQ)
	{
		return CLQuat(fScalar*rkQ.w,fScalar*rkQ.x,fScalar*rkQ.y,
			fScalar*rkQ.z);
	}
	CLQuat operator- () const;
	inline bool operator== (const CLQuat& rhs) const
	{
		return (rhs.x == x) && (rhs.y == y) &&
			(rhs.z == z) && (rhs.w == w);
	}

	// functions of a CLQuat
	float dot (const CLQuat& rkQ) const;  // dot product
	float norm () const;  // squared-length
	/// Normalises this CLQuat, and returns the previous length
	float normalise(void); 
	CLQuat inverse () const;  // apply to non-zero CLQuat
	CLQuat unitInverse () const;  // apply to unit-length CLQuat
	CLQuat exp () const;
	CLQuat log () const;

	// rotation of a vector by a CLQuat
	CLPoint3 operator* (const CLPoint3& rkVector) const;

	// spherical linear interpolation
	static CLQuat slerp (float fT, const CLQuat& rkP,
		const CLQuat& rkQ, bool shortestPath = false);

	static CLQuat slerpExtraSpins (float fT,
		const CLQuat& rkP, const CLQuat& rkQ,
		int iExtraSpins);

	// setup for spherical quadratic interpolation
	static void intermediate (const CLQuat& rkQ0,
		const CLQuat& rkQ1, const CLQuat& rkQ2,
		CLQuat& rka, CLQuat& rkB);

	// spherical quadratic interpolation
	static CLQuat squad (float fT, const CLQuat& rkP,
		const CLQuat& rkA, const CLQuat& rkB,
		const CLQuat& rkQ, bool shortestPath = false);

	// cutoff for sine near zero
	static const float ms_fEpsilon;

	// special values
	static const CLQuat ZERO;
	static const CLQuat IDENTITY;

	float w, x, y, z;

};

NAMESPACE_LABOR3D_END

#endif

